package com.innjia.base.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * HttpClient工具类
 *
 * @author shenpeng
 */
public class HttpClientUtils {
    public static final int POST_TYPE_XML = 0;

    public static final int POST_TYPE_JSON = 1;

    public static final int POST_TYPE_JSONOBJECT = 2;

    public static final int POST_TYPE_DEFAULT = 3;

    public static final int POST_TYPE_MULTIPART = -1;

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtils.class);

    /**
     * 连接超时10s
     */
    private static final int CONNECTION_TIMEOUT = 10 * 1000;

    /**
     * 读取超时20s
     */
    private static final int SO_TIMEOUT = 20 * 1000;

    /**
     * HTTP模拟GET请求并返回响应结果
     *
     * @param url
     *         请求地址及参数
     * @return String 返回结果字符串
     */
    public static String get(String url) {
        return get(url, null);
    }

    /**
     * HTTP模拟GET请求并返回响应结果
     *
     * @param url
     *         请求地址
     * @param params
     *         请求参数Map<String, String>对象
     * @return String 返回结果字符串
     */
    public static String get(String url, Map<String, String> params) {
        // 响应内容
        String respContent = "";
        // 创建默认的httpClient实例
        CloseableHttpClient httpClient = HttpClients.createDefault();
        if (null != params && params.size() > 0) {
            StringBuilder builder = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                builder.append(entry.getKey());
                builder.append("=");
                builder.append(entry.getValue());
                builder.append("&");
            }
            url += "?" + builder.toString();
        }
        // 创建org.apache.http.client.methods.HttpGet
        HttpGet httpGet = new HttpGet(url);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SO_TIMEOUT).setConnectTimeout(CONNECTION_TIMEOUT).build();
        httpGet.setConfig(requestConfig);
        try {
            // 执行GET请求
            HttpResponse response = httpClient.execute(httpGet);
            // 获取响应实体
            HttpEntity entity = response.getEntity();
            if (null != entity) {
                Charset respCharset = ContentType.getOrDefault(entity).getCharset();
                respContent = EntityUtils.toString(entity, respCharset);
                EntityUtils.consume(entity);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("--------------------------------------GET响应报文信息-----------------------------------------");
                StringBuilder respHeaderDatas = new StringBuilder();
                for (Header header : response.getAllHeaders()) {
                    respHeaderDatas.append(header.toString()).append("\r\n");
                }
                // HTTP响应状态行信息
                String respStatusLine = response.getStatusLine().toString();
                // HTTP响应报文头信息
                String respHeaderMsg = respHeaderDatas.toString().trim();
                // HTTP响应报文体信息
                String respBodyMsg = respContent;
                LOGGER.debug("请求[" + url + "]响应完整报文:\r\n" + respStatusLine + "\r\n" + respHeaderMsg + "\r\n\r\n" + respContent
                        + "\r\n");
                LOGGER.debug("-------------------------------------------------------------------------------------------");
            }
        } catch (Exception e) {
            LOGGER.error("请求[" + url + "]时偶遇异常,错误信息:", e);
        } finally {
            IOUtils.closeQuietly(httpClient);
        }
        return respContent;
    }

    /**
     * 【POST-BODY】基本POST请求通用方法,支持JSON对象传输
     *
     * @param url
     *         请求地址
     * @param json
     *         参数对象
     * @return String 返回结果字符串
     */
    public static String post(String url, JSONObject json) {
        return post(url, json, 2);
    }

    /**
     * 基本POST请求通用方法,支持JSON字符串，JSONObject对象，Map<String, Object>集合对象
     *
     * @param url
     *         请求地址
     * @param params
     *         参数对象
     * @param paramsType
     *         参数类型
     * @return String 返回结果字符串
     */
    @SuppressWarnings("deprecation")
    private static String post(String url, Object params, Integer paramsType) {
        String respContent = "";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SO_TIMEOUT).setConnectTimeout(CONNECTION_TIMEOUT).build();
        httpPost.setConfig(requestConfig);
        try {
            switch (paramsType) {
                // 使用XML字符串方式传递
                case POST_TYPE_XML:
                    StringEntity xmlEntity = new StringEntity(params.toString(), "UTF-8");
                    xmlEntity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/xml"));
                    xmlEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, "UTF-8"));
                    httpPost.setHeader("Accept", "application/xml");
                    httpPost.setHeader("Content-Type", "application/xml");
                    httpPost.setEntity(xmlEntity);
                    break;
                // 使用JSON字符串方式传递
                case POST_TYPE_JSON:
                    StringEntity jsonEntity = new StringEntity(params.toString(), "UTF-8");
                    jsonEntity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
                    jsonEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, "UTF-8"));
                    httpPost.setHeader("Accept", "application/json");
                    httpPost.setHeader("Content-Type", "application/json");
                    httpPost.setEntity(jsonEntity);
                    break;
                // 使用JSONObject对象方式传递
                case POST_TYPE_JSONOBJECT:
                    StringEntity jsonObjEntity = new StringEntity(JSONObject.toJSONString(params), "UTF-8");
                    jsonObjEntity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
                    jsonObjEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, HTTP.UTF_8));
                    httpPost.setHeader("Accept", "application/json");
                    httpPost.setHeader("Content-Type", "application/json");
                    httpPost.setEntity(jsonObjEntity);
                    break;
                case POST_TYPE_DEFAULT:
                    @SuppressWarnings("unchecked")
                    Map<String, String> mapStrParams = (Map<String, String>) params;
                    List<NameValuePair> pairs = null;
                    if (mapStrParams != null && mapStrParams.size() > 0) {
                        pairs = new ArrayList<NameValuePair>();
                        for (Map.Entry<String, String> entry : mapStrParams.entrySet()) {
                            pairs.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
                        }
                    }
                    httpPost.setEntity(new UrlEncodedFormEntity(pairs, Charset.forName("utf-8")));
                    break;
                default:
                    @SuppressWarnings("unchecked")
                    Map<String, Object> mapObjParams = (Map<String, Object>) params;
                    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                    if (mapObjParams != null && mapObjParams.size() > 0) {
                        for (Map.Entry<String, Object> entry : mapObjParams.entrySet()) {
                            if (entry.getValue() != null) {
                                if (entry.getValue() instanceof FileBody) {
                                    builder.addPart(entry.getKey(), (ContentBody) entry.getValue());
                                } else {
                                    builder.addPart(entry.getKey(), new StringBody(entry.getValue().toString(), Charset.forName("utf-8")));
                                }
                            }
                        }
                    }
                    httpPost.setEntity(builder.build());
            }
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            if (null != entity) {
                respContent = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
                EntityUtils.consume(entity);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("--------------------------------------POST响应报文信息-----------------------------------------");
                StringBuilder respHeaderDatas = new StringBuilder();
                for (Header header : response.getAllHeaders()) {
                    respHeaderDatas.append(header.toString()).append("\r\n");
                }
                String respStatusLine = response.getStatusLine().toString(); // HTTP响应状态行信息
                String respHeaderMsg = respHeaderDatas.toString().trim(); // HTTP响应报文头信息
                String respBodyMsg = respContent; // HTTP响应报文体信息
                LOGGER.debug("请求[" + url + "]响应完整报文:\r\n" + respStatusLine + "\r\n" + respHeaderMsg + "\r\n\r\n" + respBodyMsg
                        + "\r\n");
                LOGGER.debug("-------------------------------------------------------------------------------------------");
            }
        } catch (Exception e) {
            LOGGER.error("请求[" + url + "]时偶遇异常,错误信息:", e);
        } finally {
            IOUtils.closeQuietly(httpClient);
        }
        return respContent;
    }

    /**
     * 【POST-BODY】基本POST请求通用方法,支持JSON字符串传输
     *
     * @param url
     *         请求地址
     * @param json
     *         参数字符串
     * @return String 返回结果字符串
     */
    public static String postJson(String url, String json) {
        return post(url, json, 1);
    }

    /**
     * 【POST-BODY】基本POST请求通用方法,支持XML字符串传输
     *
     * @param url
     * @param xml
     * @return
     */
    public static String postXml(String url, String xml) {
        return post(url, xml, 0);
    }

    /**
     * 【POST-FORM】HTTP模拟POST请求并返回响应结果
     *
     * @param url
     *         请求地址
     * @param params
     *         请求参数Map对象（支持文件对象）
     * @return String 返回结果字符串
     */
    public static String post(String url, Map<String, Object> params) {
        return post(url, params, 3);
    }


    // ============================================私有方法=========================================================

    /**
     * 【POST-FORM】HTTP模拟POST请求并返回响应结果
     *
     * @param url
     *         请求地址
     * @param params
     *         请求参数Map对象（支持文件对象）
     * @param flag
     *         是否使用文件传输
     * @return String 返回结果字符串
     */
    public static String post(String url, Map<String, Object> params, boolean flag) {
        return post(url, params, flag ? -1 : 3);
    }

}
